/*------------------------------------------------------------------------------
 * Copyright 2012 NCR Corporation
 *------------------------------------------------------------------------------
 * All revision information is updated automatically from source code control
 * change records - please do not manually edit.
 *------------------------------------------------------------------------------
 *
 * $LastChangedRevision:: 2123                                     $
 * $LastChangedBy:: rb250096                                       $
 * $LastChangedDate:: 2012-04-18 00:43:25 -0400 (Wed, 18 Apr 2012) $
 */

package com.ncr.nep.dashboardcore.utils.client;

import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.ScriptInjector;
import com.google.web.bindery.event.shared.Event;
import com.google.web.bindery.event.shared.SimpleEventBus;

/**
 * Extension of {@link SimpleEventBus} that allows for dispatching certain
 * types of events to other JavaScript windows (like iframes).  This allows
 * for seamless communication and messaging between multiple iframes or
 * spawned browser windows.
 *
 * <p>
 *     In order to register for specific events with specific message types
 *     from other windows/frames, use the
 *     {@link #addHandlerToSource(com.google.web.bindery.event.shared.Event.Type, Object, Object)}
 *     method with the source being the message type string.  To dispatch
 *     an {@link InterWindowEvent} to other {@link InterWindowEventBus}
 *     instances, only use the
 *     {@link #fireEvent(com.google.web.bindery.event.shared.Event)} method;
 *     the {@link InterWindowEvent} will contain the required message type.
 * </p>
 *
 * @author rb250096
 * @version $Revision: 2123 $
 */
public class InterWindowEventBus extends SimpleEventBus {

    //--------------------------------------------------------------------------
    //
    //  Fields
    //
    //--------------------------------------------------------------------------

    /**
     * Instance of underlying <code>ncr.WindowBus</code> object that performs
     * messaging.
     */
    protected JavaScriptObject bus;

    //--------------------------------------------------------------------------
    //
    //  Constructors
    //
    //--------------------------------------------------------------------------

    /**
     * Constructor.
     */
    public InterWindowEventBus() {
        super();
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    /**
     * Gets boolean indicating whether this bus is the master bus that controls
     * the messaging to child buses.
     *
     * @return True if this bus is the master.
     */
    public native boolean isMaster() /*-{
         return this.@com.ncr.nep.dashboardcore.utils.client.InterWindowEventBus::bus._master;
    }-*/;

    /**
     * Gets the ID of this event bus; the ID is distributed by the master event
     * bus.
     *
     * @return The ID of the bus.
     */
    public native String getNodeId() /*-{
         return this.@com.ncr.nep.dashboardcore.utils.client.InterWindowEventBus::bus.nodeId;
    }-*/;

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     * Initializes the event bus.  This method must be called before attempting
     * to use the bus.
     *
     * @param master True to set this bus instance as the master instance.
     */
    public void init(final boolean master) {
        ScriptInjector.fromUrl(URLUtil.generateResourceURL(
                "dashboardcore/dashboardcore.min.js")).setCallback(new Callback<Void, Exception>() {
            @Override
            public void onFailure(Exception reason) {
                throw new RuntimeException(reason);
            }

            @Override
            public void onSuccess(Void result) {
                initializeBus(master);
            }
        }).setWindow(ScriptInjector.TOP_WINDOW).inject();
    }

    @Override
    public void fireEvent(Event<?> event) {
        super.fireEvent(event);

        if(event instanceof InterWindowEvent) {
            InterWindowEvent ie = (InterWindowEvent)event;
            this.dispatchToBus(ie.getMessageType(), ie.getBody(),
                    ie.isBroadcast());
        }
    }

    /**
     * Dispatches a message object using the underlying <code>WindowBus</code>
     * instance.
     *
     * @param messageType The type of message being dispatched.
     * @param body The body of the message.
     * @param broadcast True to broadcast the message to all other registered
     *                  <code>WindowBus</code> instances, false to only
     *                  dispatch to parent/master.
     */
    protected native void dispatchToBus(String messageType,
                                        Object body,
                                        boolean broadcast) /*-{
        this.@com.ncr.nep.dashboardcore.utils.client.InterWindowEventBus::bus
                .dispatch(messageType, body, broadcast);
    }-*/;

    /**
     * Initializes the underlying <code>ncr.WindowBus</code> instance.
     *
     * @param master True if this is the master bus.
     */
    protected native void initializeBus(boolean master) /*-{
        console.log("Initializing WindowBus. Master: " + master);

        var that = this;
        var f = function(msg) {
                that.@com.ncr.nep.dashboardcore.utils.client.InterWindowEventBus::busMessageReceived(Lcom/ncr/nep/dashboardcore/utils/client/BusMessage;)(msg);
            },
            r = function() {
                that.@com.ncr.nep.dashboardcore.utils.client.InterWindowEventBus::busRegistrationHandler()();
            },
            b = $wnd.ncr.extend($wnd.ncr.WindowBus, {
                msgHandler: f,
                registeredHandler: r
            });

        this.@com.ncr.nep.dashboardcore.utils.client.InterWindowEventBus::bus = b;

        b.init(master);
    }-*/;

    /**
     * Callback method invoked when a message is received from another
     * {@link InterWindowEventBus} from another window.
     *
     * @param msg The message object.
     */
    protected void busMessageReceived(BusMessage msg) {
        GWT.log("Bus message received. Type:" + msg.getType());
        InterWindowEvent ie = new InterWindowEvent(msg.getType(), msg.getSrc(),
                msg.getBody());
        this.fireEventFromSource(ie, msg.getType());
    }

    /**
     * Callback method invoked when a non-master bus instance is successfully
     * registered with the master/parent bus.  This will result in a
     * {@link InterWindowEvent} being dispatched with the source being equal
     * to the {@link InterWindowEvent#REGISTERED_EVENT_TYPE} constant.
     */
    protected void busRegistrationHandler() {
        GWT.log("Registration complete for frame.");
        this.fireEventFromSource(new InterWindowEvent(
                InterWindowEvent.REGISTERED_EVENT_TYPE, null, null),
                InterWindowEvent.REGISTERED_EVENT_TYPE);
    }
}
